using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;

namespace HalconDotNet
{
	[ToolboxBitmap(typeof(HWindowControl))]
	[Designer(typeof(HWindowControlDesigner))]
	[DefaultEvent("HMouseMove")]
	public class HWindowControl : UserControl
	{
		private const string positionDescription = " The position is returned in the image coordinate system.";

		private IntPtr hwnd = IntPtr.Zero;

		private HWindow window;

		private Rectangle imagePart = new Rectangle(0, 0, 640, 480);

		private Rectangle windowExtents = new Rectangle(0, 0, 320, 240);

		private int borderWidth;

		private Color borderColor = Color.Black;

		private PaintEventHandler paintEventDelegate;

		private Container components;

		protected override Size DefaultSize => new Size(320, 240);

		[Description("Size of the HALCON window in pixels. Without border, this will be identical to the control size")]
		[Category("Layout")]
		public Size WindowSize
		{
			get
			{
				return windowExtents.Size;
			}
			set
			{
				base.ClientSize = new Size(value.Width + 2 * borderWidth, value.Height + 2 * borderWidth);
			}
		}

		[Description("This rectangle specifies the image part to be displayed, which will automatically be zoomed to fill the window. To display a full image of size W x H, set this to 0;0;W;H")]
		[Category("Layout")]
		public Rectangle ImagePart
		{
			get
			{
				if (window != null)
				{
					window.GetPart(out int row, out int column, out int row2, out int column2);
					imagePart = new Rectangle(column, row, column2 - column + 1, row2 - row + 1);
				}
				return imagePart;
			}
			set
			{
				if (value.IsEmpty)
				{
					imagePart = new Rectangle(0, 0, base.Width - 2 * borderWidth, base.Height - 2 * BorderWidth);
				}
				else
				{
					imagePart = value;
				}
				UpdatePart();
			}
		}

		[Description("Width of optional border in pixels")]
		[DefaultValue(0)]
		[Category("Appearance")]
		public int BorderWidth
		{
			get
			{
				return borderWidth;
			}
			set
			{
				borderWidth = value;
				UpdateWindowExtents();
			}
		}

		[Category("Appearance")]
		[Description("Color of optional border around window")]
		public Color BorderColor
		{
			get
			{
				return borderColor;
			}
			set
			{
				borderColor = value;
				BackColor = borderColor;
			}
		}

		[Browsable(false)]
		public HWindow HalconWindow
		{
			get
			{
				if (window != null)
				{
					return window;
				}
				return new HWindow();
			}
		}

		[Browsable(false)]
		public IntPtr HalconID
		{
			get
			{
				if (window != null)
				{
					return window.Handle;
				}
				return IntPtr.Zero;
			}
		}

		[Browsable(false)]
		public override Color ForeColor
		{
			get
			{
				return base.ForeColor;
			}
			set
			{
				base.ForeColor = value;
			}
		}

		[Browsable(false)]
		public override Color BackColor
		{
			get
			{
				return base.BackColor;
			}
			set
			{
				base.BackColor = value;
			}
		}

		[Browsable(false)]
		public override Image BackgroundImage
		{
			get
			{
				return base.BackgroundImage;
			}
			set
			{
				base.BackgroundImage = value;
			}
		}

		[Category("Behavior")]
		[Description("Occurs after the HALCON window has been initialized.")]
		public event HInitWindowEventHandler HInitWindow;

		[Description("Occurs when the mouse is moved over the HALCON window. Note that delta is meaningless here. The position is returned in the image coordinate system.")]
		[Category("Mouse")]
		public event HMouseEventHandler HMouseMove;

		[Description("Occurs when a button is pressed over the HALCON window. Note that delta is meaningless here. The position is returned in the image coordinate system.")]
		[Category("Mouse")]
		public event HMouseEventHandler HMouseDown;

		[Category("Mouse")]
		[Description("Occurs when a button is released over the HALCON window. Note that delta is meaningless here. The position is returned in the image coordinate system.")]
		public event HMouseEventHandler HMouseUp;

		[Description("Occurs when the wheel is used over the HALCON window. Note that button is meaningless here. The position is returned in the image coordinate system.")]
		[Category("Mouse")]
		public event HMouseEventHandler HMouseWheel;

		public HWindowControl()
		{
			InitializeComponent();
			if (HalconAPI.isWindows)
			{
				createWindow(repair: false);
			}
			paintEventDelegate = HWindowControl_Paint;
			base.Paint += paintEventDelegate;
		}

		private void HWindowControl_Paint(object sender, PaintEventArgs e)
		{
			base.Paint -= paintEventDelegate;
			if (!HalconAPI.isWindows)
			{
				createWindow(repair: false);
				try
				{
					Form form = (Form)base.TopLevelControl;
					form.Closing += Form_Closing;
				}
				catch (Exception)
				{
				}
			}
			OnHInitWindow();
		}

		private void Form_Closing(object sender, CancelEventArgs e)
		{
			Dispose();
		}

		private void HWindowControl_VisibleChanged(object sender, EventArgs e)
		{
			if (window != null && base.Visible && hwnd != base.Handle)
			{
				createWindow(repair: true);
			}
		}

		private void createWindow(bool repair)
		{
			BackColor = BorderColor;
			if ((window == null || repair) && LicenseManager.UsageMode != LicenseUsageMode.Designtime)
			{
				try
				{
					HOperatorSet.SetCheck("~father");
					if (window == null)
					{
						window = new HWindow();
					}
					else
					{
						window.GetPart(out int row, out int column, out int row2, out int column2);
						imagePart = new Rectangle(column, row, column2 - column + 1, row2 - row + 1);
					}
					hwnd = base.Handle;
					window.OpenWindow(borderWidth, borderWidth, base.Width - 2 * borderWidth, base.Height - 2 * borderWidth, hwnd, "visible", "");
					UpdatePart();
				}
				catch (HOperatorException ex)
				{
					int errorCode = ex.GetErrorCode();
					if (errorCode >= 5100 && errorCode < 5200)
					{
						throw ex;
					}
				}
				catch (DllNotFoundException)
				{
				}
			}
		}

		protected override void Dispose(bool disposing)
		{
			if (disposing && window != null)
			{
				window.Dispose();
				window = null;
				if (components != null)
				{
					components.Dispose();
				}
			}
			base.Dispose(disposing);
		}

		private void InitializeComponent()
		{
			base.Name = "HWindowControl";
			base.Size = new System.Drawing.Size(320, 240);
			base.VisibleChanged += new System.EventHandler(HWindowControl_VisibleChanged);
			base.Resize += new System.EventHandler(HWindowControl_Resize);
			base.MouseUp += new System.Windows.Forms.MouseEventHandler(HWindowControl_MouseUp);
			base.MouseMove += new System.Windows.Forms.MouseEventHandler(HWindowControl_MouseMove);
			base.MouseWheel += new System.Windows.Forms.MouseEventHandler(HWindowControl_MouseWheel);
			base.MouseDown += new System.Windows.Forms.MouseEventHandler(HWindowControl_MouseDown);
		}

		private void UpdateWindowExtents()
		{
			windowExtents = new Rectangle(borderWidth, borderWidth, base.ClientSize.Width - 2 * borderWidth, base.ClientSize.Height - 2 * borderWidth);
			if (window != null && windowExtents.Width > 0 && windowExtents.Height > 0)
			{
				window.GetWindowExtents(out int row, out int column, out int width, out int height);
				if (!windowExtents.Equals(new Rectangle(row, column, width, height)))
				{
					window.SetWindowExtents(windowExtents.Left, windowExtents.Top, windowExtents.Width, windowExtents.Height);
					if (HSystem.GetSystem(new HTuple("flush_graphic")).S == "true")
					{
						Refresh();
					}
				}
			}
			else
			{
				Refresh();
			}
		}

		private void UpdatePart()
		{
			if (window != null)
			{
				window.SetPart(imagePart.Top, imagePart.Left, imagePart.Top + imagePart.Height - 1, imagePart.Left + imagePart.Width - 1);
			}
		}

		private void HWindowControl_Resize(object sender, EventArgs e)
		{
			UpdateWindowExtents();
		}

		public void SetFullImagePart(HImage reference)
		{
			reference.GetImagePointer1(out string _, out int width, out int height);
			ImagePart = new Rectangle(0, 0, width, height);
		}

		protected virtual void OnHInitWindow()
		{
			if (this.HInitWindow != null)
			{
				this.HInitWindow(this, new EventArgs());
			}
		}

		protected virtual void OnHMouseMove(HMouseEventArgs e)
		{
			if (this.HMouseMove != null)
			{
				this.HMouseMove(this, e);
			}
		}

		protected virtual void OnHMouseDown(HMouseEventArgs e)
		{
			if (this.HMouseDown != null)
			{
				this.HMouseDown(this, e);
			}
		}

		protected virtual void OnHMouseUp(HMouseEventArgs e)
		{
			if (this.HMouseUp != null)
			{
				this.HMouseUp(this, e);
			}
		}

		protected virtual void OnHMouseWheel(HMouseEventArgs e)
		{
			if (this.HMouseWheel != null)
			{
				this.HMouseWheel(this, e);
			}
		}

		private HMouseEventArgs ToHMouse(MouseEventArgs e)
		{
			double rowImage;
			double columnImage;
			if (window == null)
			{
				rowImage = (double)imagePart.Top + (double)(e.Y - borderWidth) * (double)imagePart.Height / (double)windowExtents.Height;
				columnImage = (double)imagePart.Left + (double)(e.X - borderWidth) * (double)imagePart.Width / (double)windowExtents.Width;
			}
			else
			{
				window.ConvertCoordinatesWindowToImage(e.Y - borderWidth, e.X - borderWidth, out rowImage, out columnImage);
			}
			return new HMouseEventArgs(e.Button, e.Clicks, columnImage, rowImage, e.Delta);
		}

		private void HWindowControl_MouseMove(object sender, MouseEventArgs e)
		{
			if (windowExtents.Contains(e.X, e.Y))
			{
				OnHMouseMove(ToHMouse(e));
			}
		}

		private void HWindowControl_MouseDown(object sender, MouseEventArgs e)
		{
			if (windowExtents.Contains(e.X, e.Y))
			{
				OnHMouseDown(ToHMouse(e));
			}
		}

		private void HWindowControl_MouseUp(object sender, MouseEventArgs e)
		{
			if (windowExtents.Contains(e.X, e.Y))
			{
				OnHMouseUp(ToHMouse(e));
			}
		}

		private void HWindowControl_MouseWheel(object sender, MouseEventArgs e)
		{
			if (windowExtents.Contains(e.X, e.Y))
			{
				OnHMouseWheel(ToHMouse(e));
			}
		}
	}
}
